import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ListIterator;
import java.util.Vector;
import java.util.Scanner;

/**
 * This class is the main class for the server side message processing. 
 * The class executes in it's own thread and is passed the newly created
 * connection in the constructor. 
 * @author Tom Crowe (09137149@ucdconnect.ie)
 *
 */
class WorkerThr extends Thread implements OfficeMsgs{

	private Socket clientSckt = null;
	private	Vector<Office> officeList = null;
	
	
	/**
	 * Constructor
	 * @param incoming Socket connection from/to client
	 * @param offList List of enterprises offering Office Services
	 */
	WorkerThr(Socket incoming, Vector<Office> offList){
		
		clientSckt = incoming;
		officeList = offList;
	}
	
	
	/**
	 * Entry point for start of new thread. The next message is read from the socket
	 * input stream (ascii), parsed and the appropriate response is constructed and 
	 * returned.
	 */
	public void run(){
		
		BufferedReader in = null;
		
		// Wrap a buffered reader round the raw socket input stream.
		// Then read in the message and print the message to the console
		try {

			String msgType = null;
			int offRef = 0;
			
			in = new BufferedReader(new InputStreamReader(clientSckt.getInputStream()));
			String msg = in.readLine();
			System.out.println("Received message: " + msg);		

			Scanner s = new Scanner(msg).useDelimiter(OFFICE_MSG_DELIM);
			if (s.hasNext())
				msgType = s.next();
			else
				// no valid field in msg so finish - finally clause will do the cleanup
				return;
			
			if (s.hasNext()) {
				String refStr = s.next();
				try {
					offRef = Integer.valueOf(refStr);
				}
				catch (NumberFormatException nfe){
					return;  // finally clause will do the cleanup
				}			
			}
			
			// Only three messages are allowed. Anything else and the connection is terminated straight away.
			if (msgType.compareTo(OFFICE_GET_LIST_MSG) == 0) {
				String reply = processGetList();
				sendReply(reply, clientSckt);
			}
			else if (msgType.compareTo(OFFICE_GET_INFO_MSG) == 0){
				String reply = processGetInfo(offRef);
				if (reply != null)
					sendReply(reply, clientSckt);					
			}
			else if (msgType.compareTo(OFFICE_GET_MAP_MSG) == 0){
				String reply = processGetMap(offRef);
				if (reply != null)
					sendReply(reply, clientSckt);					
			}
		}
		catch (IOException ioe) {
			ioe.printStackTrace();
		}
		finally {
			try {
				in.close();
				clientSckt.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
	}

	
	/**
	 * Return a list of premises offering Office Services.
	 * @return The list of businesses offering Office Services.
	 */
	private String processGetList() {

		StringBuilder resp = new StringBuilder();
		Office currOff = null;
		ListIterator<Office> iter = officeList.listIterator();
		
		// Add the msg header and a count of the number of Offices as the first msg field
		resp.append(OFFICE_LIST_MSG + OFFICE_MSG_DELIM);
		resp.append(officeList.size() + OFFICE_MSG_DELIM);

		while (iter.hasNext()){
			currOff = iter.next();
			resp.append(currOff.getMainInfoAsDelimString(OFFICE_MSG_DELIM));	
		}
		return resp.toString();
	}
	
	
	/**
	 * Returns a list of office services for a given enterprise
	 * @param offRef Reference to a given enterprise offering Office Services
	 * @return List of Office Services
	 */
	private String processGetInfo(int offRef) {

		StringBuilder resp = null;
		Office currOff = null;
		boolean found = false;
		ListIterator<Office> iter = officeList.listIterator();

		while (iter.hasNext()){
			currOff = iter.next();
			if (offRef == currOff.getRef()){
				found = true;
				break;
			}
		}
		
		if (!found)
			return null;
		
		// Add the msg header, office ref and a count of the number of Offices as the first msg field
		resp = new StringBuilder();
		resp.append(OFFICE_INFO_MSG + OFFICE_MSG_DELIM);
		resp.append(currOff.getRef() + OFFICE_MSG_DELIM);
		resp.append(currOff.getOffServices().size() + OFFICE_MSG_DELIM);
		resp.append(currOff.getOffServicesDelimString(OFFICE_MSG_DELIM));	

		return resp.toString();
	}

	
	/**
	 * Returns a map outlining the location of the Office Services
	 * @param offRef A reference number for a given Office.
	 * @return An ascii-encoded binary string containing the map.
	 */
	private String processGetMap(int offRef){
		return "null";
	}

	
	/**
	 * Sends a reply back to the client.
	 * @param retMsg The message to send to the client.
	 * @param sock The server socket allocated to this connection.
	 * @throws IOException
	 * @throws SecurityException
	 */
	private void sendReply(String retMsg, Socket sock) throws IOException, SecurityException {

		// Send the response back to the client.;
		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
		out.write(retMsg,0,retMsg.length());
		out.newLine();
		out.flush();
		System.out.println("Returned message: " + retMsg);

		// Tidy up
		out.close();

	}
	
}
